package com.zhaokuner.parentstarter.handler;

import com.zhaokuner.parentstarter.common.ResultData;
import com.zhaokuner.parentstarter.exception.BusinessException;
import com.zhaokuner.parentstarter.resultcode.SuperResultCodeUtil;
import com.zhaokuner.parentstarter.utils.JacksonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 类描述:错误拦截
 *
 * @author 赵希坤 zhaoxikun
 * @date 2020/12/11 10:16
 */

public class MyHandlerExceptionResolver implements HandlerExceptionResolver, ApplicationContextAware {
    private static final Logger log = LoggerFactory.getLogger(MyHandlerExceptionResolver.class);

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        //不是想要的
        if (!(handler instanceof HandlerMethod)) {
            return null;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;

        ResultData resultData = null;
        //是我们控制的ctrl 或 我们抛出的异常
        switch (ex.getClass().getSimpleName()) {
            case "HttpMessageNotReadableException":
            case "ServletRequestBindingException":
                resultData = handleParamsException(ex, request);
                break;
            case "MethodArgumentNotValidException":
                resultData = handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request);
                break;
            case "BindException":
                resultData = handleBindException((BindException) ex, request);
                break;
            case "BusinessException":
                resultData = handleBusinessException((BusinessException) ex, request);
                break;
            default:
                return null;
        }
        if (null != resultData) {
            try {
                response.setContentType("application/json;charset=utf-8");
                response.getWriter().write(JacksonUtils.toJson(resultData));
                return new ModelAndView();
            } catch (IOException e1) {
                throw new RuntimeException(e1);
            }
        }
        return null;
    }

    /**
     * 方法描述：参数组装对象失败
     * 包含cookie参数CookieValue
     *
     * @param e
     * @param request
     * @return ResultData
     * @author 赵希坤
     * @date 2020/12/15 15:29
     */
    @ExceptionHandler({HttpMessageNotReadableException.class, ServletRequestBindingException.class})
    @ResponseStatus(HttpStatus.OK)
    public ResultData handleParamsException(Exception e, HttpServletRequest request) {
        //信息本地化
        String message = getMessage(request, SuperResultCodeUtil.INVALID_PARAM_VALUE.getMsg());
        return ResultData.error(SuperResultCodeUtil.INVALID_PARAM_VALUE, message);
    }

    /**
     * 方法描述：RequestBody 参数验证失败
     *
     * @param e
     * @param request
     * @return ResultData
     * @author 赵希坤
     * @date 2020/12/15 15:29
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultData handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        String message = e.getMessage();
        BindingResult bindingResult = e.getBindingResult();
        FieldError error = bindingResult.getFieldError();
        if (null != error) {
            message = error.getDefaultMessage();
        }
        //信息本地化
        message = getMessage(request, message);
        return ResultData.error(SuperResultCodeUtil.INVALID_PARAM_VALUE, message);
    }

    /**
     * 方法描述：路径参数 验证不通过
     *
     * @param e
     * @param request
     * @return ResultData
     * @author 赵希坤
     * @date 2020/12/15 15:29
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultData handleBindException(BindException e, HttpServletRequest request) {
        String message = e.getMessage();
        BindingResult bindingResult = e.getBindingResult();
        FieldError error = bindingResult.getFieldError();
        if (null != error) {
            message = error.getDefaultMessage();
        }
        //信息本地化
        message = getMessage(request, message);
        return ResultData.error(SuperResultCodeUtil.INVALID_PARAM_VALUE, message);
    }


    /**
     * 统一处理业务异常（业务异常都是编译异常，非运行时异常），因为是业务异常，也就是它仅仅是一个需求上的一个业务分支，并不是程序出错了，
     * <p>
     * 所以是不应该返回5xx异常，所以返回给客户端的HTTPStatus应该依然是200。——但是似乎很多人并没有想明白编译异常与运行时异常的区别。
     *
     * @param e       异常
     * @param request 请求
     * @return 数据
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultData handleBusinessException(BusinessException e, HttpServletRequest request) {
        //信息本地化
        String message = getMessage(request, e.getMsg());
        e.setMsg(message);
        log.info("BusinessException: code:{} msg:{}", e.getCode(), message);
        return ResultData.error(e);
    }

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    }

    private String getMessage(HttpServletRequest request, String msg) {
        return msg;
    }
}
